#ifndef COMMAND_H
#define COMMAND_H

#include <wx/object.h>
#include <wx/arrstr.h>
#include <wx/tokenzr.h>
#include <wx/hashmap.h>
#include <wx/thread.h>

#include "tux.h"
#include "Util.h"



/***************************************************************************
The base class for msn command
****************************************************************************/
class MsnCommand : public wxObject
{
public:
    wxString type;
    wxArrayString parameterList;
	int transactionID;
	bool inTransaction;

public:
    MsnCommand() : inTransaction(false){}
	MsnCommand(bool tr) : inTransaction(tr){}
	MsnCommand(const wxString& cmd, bool tr);
    virtual ~MsnCommand(){};

	wxString GetType(){return type;};
    int GetTransactionID(){return transactionID;};

public:
    void Parse(const wxString& line);

    wxString GetParameter(int index);
    int GetParameterAsInt(int index);

    void SetParameter(const wxString& value);
    void SetParameter(int value);

    void SetParameter(int index, const wxString& value);

    int GetParameterCount();

    wxString toString();
    void ToBuffer(wxMemoryBuffer& buf);
};


class MsnCommandWrapper
{
private:
	wxString type;

	MsnCommand* command;

	wxMemoryBuffer payloadBuffer;

public:
	MsnCommandWrapper(MsnCommand* cmd):command(cmd){ type = cmd->GetType(); }
	~MsnCommandWrapper(){delete command;}

	MsnCommand* GetCommand(){return command;}

	bool IsPayloadCommand(){return type.IsSameAs(_T("MSG"));}

	int GetPayloadLength()
	{
		if(IsPayloadCommand()){
			return command->GetParameterAsInt(command->GetParameterCount()-1);
		}

		return 0;
	}

	wxMemoryBuffer& GetPayloadBuffer(){return payloadBuffer;}
};

/***************************************************************************
The in command
****************************************************************************/
class InCommandVER : public MsnCommand
{
public:
	InCommandVER() : MsnCommand(true){}

    wxString GetVersion(){return GetParameter(0);}

DECLARE_DYNAMIC_CLASS(InCommandVER)
};

class InCommandCVR : public MsnCommand
{
public:
	InCommandCVR() : MsnCommand(true){}

DECLARE_DYNAMIC_CLASS(InCommandCVR)
};

class InCommandXFR : public MsnCommand
{
public:
	InCommandXFR() : MsnCommand(true){}

    bool IsSwitch(){return GetParameter(0).IsSameAs(_T("SB"));}
    bool IsNotification(){return GetParameter(0).IsSameAs(_T("NS"));}

    wxString GetServerAddress(){
        wxString server = GetParameter(1);
        return server.BeforeFirst(':');
    }
    int GetServerPort(){
        wxString server = GetParameter(1);
        return atoi(server.AfterFirst(':').mb_str());
    }
    wxString GetAuthString(){return GetParameter(3);}

DECLARE_DYNAMIC_CLASS(InCommandXFR)
};

class InCommandUSR : public MsnCommand
{
public:
	InCommandUSR():MsnCommand(true){}

    bool IsOK(){return GetParameter(0).IsSameAs(_T("OK"));}
    wxString GetAuthString(){return GetParameter(2);}

    // Final response from server
    wxString GetPassport(){return GetParameter(1);}
    wxString GetName(){return GetParameter(2);}

DECLARE_DYNAMIC_CLASS(InCommandUSR)
};


class InCommandSYN : public MsnCommand
{
public:
	InCommandSYN():MsnCommand(true){}

    wxString GetVersion(){return GetParameter(0)+_T(" ")+GetParameter(1);}
    int GetGroupCount(){return GetParameterAsInt(3);}
    int GetContactCount(){return GetParameterAsInt(2);}

DECLARE_DYNAMIC_CLASS(InCommandSYN)
};

class InCommandLSG : public MsnCommand
{
public:
	InCommandLSG() : MsnCommand(false){}

    wxString GetGroupName(){ return GetParameter(0); }
    wxString GetGroupID(){ return GetParameter(1); }

DECLARE_DYNAMIC_CLASS(InCommandLSG)
};

class InCommandLST : public MsnCommand
{
public:
	InCommandLST():MsnCommand(false){}

    wxString GetPassport(){return GetParameter(0).AfterFirst('=');}
    wxString GetName(){return GetParameter(1).AfterFirst('=');}
    wxString GetContactID(){
        if(GetParameter(2).StartsWith(_T("C=")))
            return GetParameter(2).AfterFirst('=');
        else
            return wxEmptyString;
    }
    int GetListNumber(){
        if(GetParameter(2).StartsWith(_T("C=")))
            return GetParameterAsInt(3);
        else
            return GetParameterAsInt(1);
    }

    wxArrayString GetGroupID(){
        wxString groups = GetParameter(4);
        wxArrayString groupIDList;
        wxStringTokenizer st(groups, _T(","));
        while(st.HasMoreTokens()){
            groupIDList.Add(st.GetNextToken());
        }
        return groupIDList;
    }

DECLARE_DYNAMIC_CLASS(InCommandLST)
};

class InCommandILN : public MsnCommand
{
public:
	InCommandILN():MsnCommand(true){}
    wxString GetStatus(){return GetParameter(0);}
    wxString GetPassport(){return GetParameter(1);}
    wxString GetName(){return GetParameter(2);}
    wxString GetClientID(){return GetParameter(3);}
	wxString GetMsnObject(){return GetParameter(4);}

DECLARE_DYNAMIC_CLASS(InCommandILN)
};

class InCommandNLN : public MsnCommand
{
public:
	InCommandNLN():MsnCommand(false){}

    wxString GetStatus(){return GetParameter(0);}
    wxString GetPassport(){return GetParameter(1);}
    wxString GetName(){return GetParameter(2);}
    wxString GetClientID(){return GetParameter(3);}
	wxString GetMsnObject(){return GetParameter(4);}

DECLARE_DYNAMIC_CLASS(InCommandNLN)
};

class InCommandFLN : public MsnCommand
{

public:
	InCommandFLN():MsnCommand(false){}

    wxString GetPassport(){return GetParameter(0);}

DECLARE_DYNAMIC_CLASS(InCommandFLN)
};

class InCommandCHG : public MsnCommand
{
public:
	InCommandCHG():MsnCommand(true){}

    wxString GetStatus(){return GetParameter(0);}
    wxString GetClientID(){return GetParameter(1);}

DECLARE_DYNAMIC_CLASS(InCommandCHG)
};

class InCommandCHL : public MsnCommand
{

public:
	InCommandCHL():MsnCommand(true){}

    wxString GetChallengeString(){return GetParameter(0);}

DECLARE_DYNAMIC_CLASS(InCommandCHL)
};

class InCommandPRP : public MsnCommand
{

public:
	InCommandPRP():MsnCommand(false){}

	//check if the first parameter is number. For initial incoming PRP, there's no tr ID. 
	bool IsInitial(){return !GetParameter(0).IsNumber();}

    wxString GetType()
	{
		if(IsInitial())
			return GetParameter(0);
		else
			return GetParameter(1);
	}
    wxString GetValue()
	{
		if(IsInitial())
			return GetParameter(1);
		else
			return GetParameter(2);	
	}

DECLARE_DYNAMIC_CLASS(InCommandPRP)
};

//intial add contact and move contact to group
class InCommandADC : public MsnCommand
{
public:
	InCommandADC():MsnCommand(true){}

    wxString GetList(){return GetParameter(0);}
    wxString GetPassport(){
        if(GetParameter(1).StartsWith(_T("N=")))
            return GetParameter(1).AfterFirst('=');
        
		return wxEmptyString;
    }

    wxString GetName(){
        if(GetParameter(2).StartsWith(_T("F=")))
            return GetParameter(2).AfterFirst('=');

        return wxEmptyString;
    }

    wxString GetContactID(){
        if(parameterList.Count() > 3)
            return GetParameter(3).AfterFirst('=');
        else
            return GetParameter(1).AfterFirst('=');
    }

    wxString GetGroupID(){
        if(parameterList.Count() > 3)
            return wxEmptyString;
        else
            return GetParameter(2);
    }

DECLARE_DYNAMIC_CLASS(InCommandADC)
};

class InCommandADG : public MsnCommand
{
public:
	InCommandADG() : MsnCommand(true){}

    wxString GetGroupName(){return GetParameter(0);}
    wxString GetGroupID(){return GetParameter(1);}

DECLARE_DYNAMIC_CLASS(InCommandADG)
};

class InCommandREM : public MsnCommand
{
public:
	InCommandREM() : MsnCommand(true){}	

	//remove from FL
    wxString GetList(){return GetParameter(0);}
    wxString GetContactID(){return GetParameter(1);}
    wxString GetGroupID(){return GetParameter(2);}

	//remove from RL,AL,BL
	wxString GetPassport(){return GetParameter(1);}

DECLARE_DYNAMIC_CLASS(InCommandREM)
};

class InCommandRMG : public MsnCommand
{
public:
	InCommandRMG() : MsnCommand(true){}

    wxString GetGroupID(){return GetParameter(0);}

DECLARE_DYNAMIC_CLASS(InCommandRMG)
};

class InCommandREG : public MsnCommand
{
public:
	InCommandREG():MsnCommand(true){}

    wxString GetGroupID(){return GetParameter(0);}
    wxString GetGroupName(){return GetParameter(1);}

DECLARE_DYNAMIC_CLASS(InCommandREG)
};

class InCommandMSG : public MsnCommand
{
public:
	InCommandMSG():MsnCommand(false){}

    wxString GetPassport(){return GetParameter(0);}
    wxString GetName(){return GetParameter(1);}
    int GetLength(){return GetParameterAsInt(2);}

DECLARE_DYNAMIC_CLASS(InCommandMSG)
};

class InCommandACK : public MsnCommand
{
public:
	InCommandACK() : MsnCommand(true){}

DECLARE_DYNAMIC_CLASS(InCommandACK)
};

class InCommandNAK : public MsnCommand
{
public:
	InCommandNAK() : MsnCommand(true){}

DECLARE_DYNAMIC_CLASS(InCommandNAK)
};


class InCommandRNG : public MsnCommand
{
public:
	InCommandRNG():MsnCommand(false){}

    wxString GetSessionID(){return GetParameter(0);}
    wxString GetServerAddress(){
        wxString server = GetParameter(1);
        return server.BeforeFirst(':');
    }
    int GetServerPort(){
        wxString server = GetParameter(1);
        return atoi(server.AfterFirst(':').mb_str());
    }
    wxString GetAuthType(){return GetParameter(2);}
    wxString GetTicket(){return GetParameter(3);}
    wxString GetInvitePassport(){return GetParameter(4);}
    wxString GetInviteName(){return GetParameter(5);}

DECLARE_DYNAMIC_CLASS(InCommandRNG)
};

/*
class InCommandQRY : public MsnCommand
{
public:
	InCommandQRY():MsnCommand(true){}

DECLARE_DYNAMIC_CLASS(InCommandQRY)
};
*/

class InCommandCAL : public MsnCommand
{
public:
	InCommandCAL():MsnCommand(true){}

DECLARE_DYNAMIC_CLASS(InCommandCAL)
};

class InCommandJOI : public MsnCommand
{

public:
	InCommandJOI():MsnCommand(false){}

    wxString GetPassport(){return GetParameter(0);}
    wxString GetName(){return GetParameter(1);}

DECLARE_DYNAMIC_CLASS(InCommandJOI)
};

class InCommandANS : public MsnCommand
{
public:
	InCommandANS():MsnCommand(true){}

DECLARE_DYNAMIC_CLASS(InCommandANS)
};

class InCommandIRO : public MsnCommand
{
DECLARE_DYNAMIC_CLASS(InCommandIRO)
public:
	InCommandIRO():MsnCommand(true){}

    wxString GetPassport(){return GetParameter(2);}
    wxString GetName(){return GetParameter(3);}

};

class InCommandBYE : public MsnCommand
{
public:
	InCommandBYE():MsnCommand(false){}

	wxString GetPassport(){return GetParameter(0);}

DECLARE_DYNAMIC_CLASS(InCommandBYE)
};

class InCommandFIL : public MsnCommand
{

public:
	InCommandFIL() : MsnCommand(false){}

    int GetFileSize(){return GetParameterAsInt(0);}

DECLARE_DYNAMIC_CLASS(InCommandFIL)
};

class InCommandOther : public MsnCommand
{

};


/***************************************************************************
The out command
****************************************************************************/

class OutCommandVER : public MsnCommand
{
public:
    OutCommandVER():MsnCommand(_T("VER"), true){}
    void SetVersion(const wxString& ver){SetParameter(ver);}
};

class OutCommandCVR : public MsnCommand
{
public:
    OutCommandCVR():MsnCommand(_T("CVR"), true){}

    void SetPassport(const wxString& account){SetParameter(account);}
    void SetClientInfo(const wxString& info){SetParameter(info);}
};

class OutCommandXFR : public MsnCommand
{
public:
    OutCommandXFR():MsnCommand(_T("XFR"), true)
	{
		SetParameter(_T("SB"));
	}
};

class OutCommandNotifyUSR: public MsnCommand
{
public:
    OutCommandNotifyUSR():MsnCommand(_T("USR"), true)
	{
		SetParameter(_T("TWN"));
		SetParameter(_T("I"));
	}

    void SetPassport(const wxString& account){SetParameter(account);}
};

class OutCommandAuthUSR : public MsnCommand
{
public:
    OutCommandAuthUSR():MsnCommand(_T("USR"), true)
	{
		SetParameter(_T("TWN"));
		SetParameter(_T("S"));
	}

    void SetTicket(const wxString& ticket){SetParameter(ticket);}
};

class OutCommandSwitchUSR : public MsnCommand
{
public:
    OutCommandSwitchUSR():MsnCommand(_T("USR"), true){}

    void SetPassport(const wxString& account){SetParameter(account);}
    void SetTicket(const wxString& ticket){SetParameter(ticket);}
};

class OutCommandSYN : public MsnCommand
{
public:
    OutCommandSYN():MsnCommand(_T("SYN"), true){}

    void SetVersion(const wxString& version){SetParameter(version);}
};

class OutCommandQRY : public MsnCommand
{
public:
    OutCommandQRY():MsnCommand(_T("QRY"), true){}

    void SetClient(const wxString& client){SetParameter(client);}
    void SetLength(int len){SetParameter(len);}
};

class OutCommandPNG : public MsnCommand
{
public:
    OutCommandPNG():MsnCommand(_T("PNG"), false){}
};

class OutCommandCHG : public MsnCommand
{
public:
    OutCommandCHG():MsnCommand(_T("CHG"), true){}

    void SetStatus(const wxString& status){SetParameter(status);}
    void SetClientID(const wxString& clientID){SetParameter(clientID);}
	void SetMsnObject(const wxString& msnObject){SetParameter(StringUtil::URLEncode(msnObject));}
};

class OutCommandADC : public MsnCommand
{
public:
    OutCommandADC():MsnCommand(_T("ADC"), true){}

    void SetList(const wxString& list){SetParameter(list);}
    void SetPassport(const wxString& passport){SetParameter(_T("N=")+passport);}
    void SetName(const wxString& name){SetParameter(_T("F=")+name);}
    void SetContactID(const wxString& contactID){SetParameter(_T("C=")+contactID);}
    void SetGroupID(const wxString& groupID){SetParameter(groupID);}
};

class OutCommandREM : public MsnCommand
{
public:
    OutCommandREM():MsnCommand(_T("REM"), true){}

    void SetList(const wxString& list){SetParameter(list);}
    void SetContactID(const wxString& contactID){SetParameter(contactID);}
    void SetGroupID(const wxString& groupID){SetParameter(groupID);}
};

class OutCommandPRP : public MsnCommand
{
public:
    OutCommandPRP():MsnCommand(_T("PRP"), true)
	{
		SetParameter(_T("MFN"));
	}

    void SetName(const wxString& name)
    {
        SetParameter(StringUtil::URLEncode(name));
    }
};

class OutCommandADG : public MsnCommand
{
public:
    OutCommandADG():MsnCommand(_T("ADG"), true){}

    void SetGroupName(const wxString& name)
    {
        SetParameter(StringUtil::URLEncode(name));
        SetParameter(_T("0"));
    }
};

class OutCommandRMG : public MsnCommand
{
public:
    OutCommandRMG():MsnCommand(_T("RMG"), true){}

    void SetGroupID(const wxString& groupID){SetParameter(groupID);}
};

class OutCommandREG : public MsnCommand
{
public:
    OutCommandREG():MsnCommand(_T("REG"), true){}

    void SetGroupID(const wxString& groupID){SetParameter(groupID);}
    void SetGroupName(const wxString& groupName)
    {
        SetParameter(StringUtil::URLEncode(groupName));
    }
};


//switch board
class OutCommandANS : public MsnCommand
{
public:
    OutCommandANS():MsnCommand(_T("ANS"), true){}

    void SetPassport(const wxString& passport){SetParameter(passport);}
    void SetTicket(const wxString& ticket){SetParameter(ticket);}
    void SetSessionID(const wxString& sessionID){SetParameter(sessionID);}
};

class OutCommandCAL : public MsnCommand
{
public:
    OutCommandCAL():MsnCommand(_T("CAL"), true){}

    void SetPassport(const wxString& passport){SetParameter(passport);}
};

class OutCommandMSG : public MsnCommand
{
public:
    OutCommandMSG():MsnCommand(_T("MSG"), true){}

    void SetAckType(const wxString& t){SetParameter(t);}
    void SetLength(int len){SetParameter(len);}
};


class MsnCommandFactory
{
public:
    static MsnCommandFactory& Get();
    MsnCommand* CreateCommand(const wxString& line);

	int NextTransactionID();

private:
    MsnCommandFactory();
	int value;
	wxMutex mutex;
};

#endif
